package com.wechat.file.util;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.math.ec.custom.sec.SecT113Field;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Component
@Slf4j
public class FileUtil {

    private static final Map<String, String> EXTENSION_TO_TYPE;
    static {
        EXTENSION_TO_TYPE = new HashMap<>();

        // 图片类型
        EXTENSION_TO_TYPE.put("jpg", "image");
        EXTENSION_TO_TYPE.put("jpeg", "image");
        EXTENSION_TO_TYPE.put("png", "image");
        EXTENSION_TO_TYPE.put("gif", "image");
        EXTENSION_TO_TYPE.put("bmp", "image");
        EXTENSION_TO_TYPE.put("tiff", "image");
        EXTENSION_TO_TYPE.put("ico", "image");
        EXTENSION_TO_TYPE.put("svg", "image");
        EXTENSION_TO_TYPE.put("webp", "image");
        EXTENSION_TO_TYPE.put("heic", "image");

        // 文档类型
        EXTENSION_TO_TYPE.put("pdf", "document");
        EXTENSION_TO_TYPE.put("doc", "document");
        EXTENSION_TO_TYPE.put("docx", "document");
        EXTENSION_TO_TYPE.put("xls", "document");
        EXTENSION_TO_TYPE.put("xlsx", "document");
        EXTENSION_TO_TYPE.put("ppt", "document");
        EXTENSION_TO_TYPE.put("pptx", "document");
        EXTENSION_TO_TYPE.put("txt", "document");
        EXTENSION_TO_TYPE.put("rtf", "document");
        EXTENSION_TO_TYPE.put("odt", "document");
        EXTENSION_TO_TYPE.put("ods", "document");
        EXTENSION_TO_TYPE.put("odp", "document");

        // 视频类型
        EXTENSION_TO_TYPE.put("mp4", "video");
        EXTENSION_TO_TYPE.put("avi", "video");
        EXTENSION_TO_TYPE.put("mov", "video");
        EXTENSION_TO_TYPE.put("mkv", "video");
        EXTENSION_TO_TYPE.put("wmv", "video");
        EXTENSION_TO_TYPE.put("flv", "video");
        EXTENSION_TO_TYPE.put("webm", "video");
        EXTENSION_TO_TYPE.put("mpeg", "video");
        EXTENSION_TO_TYPE.put("mpg", "video");

        // 压缩包类型
        EXTENSION_TO_TYPE.put("zip", "archive");
        EXTENSION_TO_TYPE.put("rar", "archive");
        EXTENSION_TO_TYPE.put("7z", "archive");
        EXTENSION_TO_TYPE.put("tar", "archive");
        EXTENSION_TO_TYPE.put("gz", "archive");
        EXTENSION_TO_TYPE.put("bz2", "archive");
        EXTENSION_TO_TYPE.put("xz", "archive");
        EXTENSION_TO_TYPE.put("iso", "archive");
    }

    // 合法的文件类型
    private static final Set<String> VALID_TYPES = Set.of(
            "image", "document", "video", "audio", "archive"
    );

    // 危险文件扩展名
    private static final Set<String> DANGEROUS_EXTENSIONS = Set.of(
            "exe", "bat", "cmd", "com", "pif", "scr", "vbs", "js", "jar",
            "jsp", "php", "asp", "aspx", "sh", "bash", "ps1"
    );

    /**
     * 获取文件扩展名
     *
     * @param filename 文件名
     * @return 扩展名（小写，不包含点号）
     */
    public static String getFileExtension(String filename) {
        if (StrUtil.isEmpty(filename)) {
            return "";
        }

        int lastDotIndex = filename.lastIndexOf('.');
        if (lastDotIndex == -1 || lastDotIndex == filename.length() - 1) {
            return "";
        }

        return filename.substring(lastDotIndex + 1).toLowerCase();
    }

    /**
     * 从MultipartFile获取文件扩展名
     */
    public static String getFileExtension(MultipartFile file) {
        if (file == null) {
            return "";
        }
        return getFileExtension(file.getOriginalFilename());
    }

    /**
     * 获取文件类型
     *
     * @param filename 文件名
     * @return 文件类型（image/document/video/audio/archive/other）
     */
    public static String getFileType(String filename) {
        String extension = getFileExtension(filename);
        return EXTENSION_TO_TYPE.getOrDefault(extension, "other");
    }

    /**
     * 从MultipartFile获取文件类型
     */
    public static String getFileType(MultipartFile file) {
        if (file == null) {
            return "other";
        }
        return getFileType(file.getOriginalFilename());
    }

    /**
     * 根据MIME类型获取文件类型
     */
    public static String getFileTypeByMime(String mimeType) {
        if (StrUtil.isEmpty(mimeType)) {
            return "other";
        }

        String lowerMime = mimeType.toLowerCase();

        if (lowerMime.startsWith("image/")) {
            return "image";
        } else if (lowerMime.startsWith("video/")) {
            return "video";
        } else if (lowerMime.startsWith("audio/")) {
            return "audio";
        } else if (lowerMime.contains("pdf") || lowerMime.contains("document") ||
                lowerMime.contains("text") || lowerMime.contains("officedocument")) {
            return "document";
        } else if (lowerMime.contains("zip") || lowerMime.contains("rar") ||
                lowerMime.contains("archive")) {
            return "archive";
        }

        return "other";
    }

    /**
     * 判断文件类型是否合法
     *
     * @param filename 文件名
     * @return true=合法，false=不合法
     */
    public static boolean isValidFileType(String filename) {
        String extension = getFileExtension(filename);

        // 检查是否为危险文件
        if (DANGEROUS_EXTENSIONS.contains(extension)) {
            log.warn("检测到危险文件类型: {}", extension);
            return false;
        }

        // 检查是否为支持的文件类型
        String fileType = getFileType(filename);
        return VALID_TYPES.contains(fileType);
    }

    /**
     * 判断文件类型是否合法（从MultipartFile）
     */
    public static boolean isValidFileType(MultipartFile file) {
        if (file == null) {
            return false;
        }
        return isValidFileType(file.getOriginalFilename());
    }

    /**
     * 判断指定类型的文件是否合法
     *
     * @param filename 文件名
     * @param expectedType 期望的文件类型
     * @return true=匹配，false=不匹配
     */
    public static boolean isValidFileType(String filename, String expectedType) {
        if (!isValidFileType(filename)) {
            return false;
        }

        String actualType = getFileType(filename);
        return expectedType.equalsIgnoreCase(actualType);
    }

    /**
     * 检查文件扩展名是否在允许列表中
     */
    public static boolean isAllowedExtension(String filename, Set<String> allowedExtensions) {
        String extension = getFileExtension(filename);
        return allowedExtensions.contains(extension);
    }

    /**
     * 获取所有支持的文件类型
     */
    public static Set<String> getSupportedFileTypes() {
        return new HashSet<>(VALID_TYPES);
    }

    /**
     * 获取指定类型支持的扩展名
     */
    public static Set<String> getSupportedExtensions(String fileType) {
        return EXTENSION_TO_TYPE.entrySet().stream()
                .filter(entry -> entry.getValue().equals(fileType))
                .map(Map.Entry::getKey)
                .collect(Collectors.toSet());
    }

    /**
     * 格式化文件大小
     */
    public static String formatFileSize(long bytes) {
        if (bytes < 0) {
            return "0 B";
        }

        final String[] units = {"B", "KB", "MB", "GB", "TB"};
        final long threshold = 1024;

        if (bytes < threshold) {
            return bytes + " " + units[0];
        }

        int unitIndex = (int) (Math.log(bytes) / Math.log(threshold));
        unitIndex = Math.min(unitIndex, units.length - 1);

        double size = bytes / Math.pow(threshold, unitIndex);
        return String.format("%.1f %s", size, units[unitIndex]);
    }

    /**
     * 生成安全的文件名
     * 移除或替换危险字符
     */
    public static String sanitizeFilename(String filename) {
        if (StrUtil.isEmpty(filename)) {
            return "unnamed_file";
        }

        // 移除危险字符
        String sanitized = filename.replaceAll("[<>:\"/\\\\|?*]", "_");

        // 移除前后空格和点号
        sanitized = sanitized.trim().replaceAll("^\\.*|\\.*$", "");

        // 长度限制
        if (sanitized.length() > 255) {
            String extension = getFileExtension(sanitized);
            String nameWithoutExt = sanitized.substring(0, sanitized.lastIndexOf('.'));
            int maxNameLength = 255 - extension.length() - 1;
            sanitized = nameWithoutExt.substring(0, Math.min(nameWithoutExt.length(), maxNameLength))
                    + "." + extension;
        }

        // 如果文件名为空，给一个默认名称
        if (sanitized.isEmpty()) {
            sanitized = "unnamed_file";
        }

        return sanitized;
    }

    /**
     * 检查文件名是否安全
     */
    public static boolean isSecureFilename(String filename) {
        if (StrUtil.isEmpty(filename)) {
            return false;
        }

        // 检查危险字符
        String[] dangerousChars = {"../", "..\\", "<", ">", ":", "\"", "|", "?", "*"};
        for (String dangerousChar : dangerousChars) {
            if (filename.contains(dangerousChar)) {
                return false;
            }
        }

        // 检查系统保留字
        String[] reservedNames = {"CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3",
                "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
                "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6",
                "LPT7", "LPT8", "LPT9"};

        String nameWithoutExt = filename.contains(".") ?
                filename.substring(0, filename.lastIndexOf('.')) : filename;

        for (String reserved : reservedNames) {
            if (reserved.equalsIgnoreCase(nameWithoutExt)) {
                return false;
            }
        }

        return true;
    }
}